﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using Framework.Reflection;
using Framework.Extensions;
using System.Data;

namespace Framework.Data
{
    public class DynamicQuery
    {
        IDatabase _database;

        protected IDatabaseQueryingImplementationDetails ImplementationDetails { get { return _database.QueryingImplementationDetails; } }

        public DynamicQuery(IDatabase database)
        {
            _database = database;
        }

        public T Scalar<T>(string sql, params object[] args)
        {
            return (T)Scalar(sql, args);
        }

        public object Scalar(string sql, params object[] args)
        {
            object result = null;
            using (var conn = OpenConnection())
            {
                result = CreateCommand(sql, conn, args).ExecuteScalar();
            }
            return result;
        }

        public void NonQuery(string sql, params object[] args)
        {
            using (var conn = OpenConnection())
            {
                CreateCommand(sql, conn, args).ExecuteNonQuery();
            }
        }

        public IEnumerable<T> Query<T>(string sql, params object[] args) where T : new()
        {
            return DynamicQ(sql, args).Select(FastObjectAccessor.DynamicTo<T>);
        }

        public IEnumerable<dynamic> DynamicQ(string sql, params object[] args)
        {
           // var res = new List<dynamic>();
            using (var conn = OpenConnection())
            {
                var rdr = CreateCommand(sql, conn, args).ExecuteReader(CommandBehavior.CloseConnection);
                while (rdr.Read())
                {
                    yield return rdr.RecordToExpando();
                    //res.Add(rdr.RecordToExpando());
                }
            }
           // return res;
        }

        DbConnection OpenConnection()
        {
            var conn = _database.ProviderFactory.CreateConnection();
            conn.ConnectionString = _database.ConnectionString;
            conn.Open();
            return conn;
        }

        DbCommand CreateCommand(string sql, DbConnection conn, params object[] args)
        {
            DbCommand result = null;
            result = _database.ProviderFactory.CreateCommand();
            result.Connection = conn;
            result.CommandText = sql;
            if (args != null && args.Length > 0) result.AddParams(args);
            return result;
        }


        int Execute(DbCommand command)
        {
            return Execute(new DbCommand[] { command });
        }
        /// <summary>
        /// Executes a series of DBCommands in a transaction
        /// </summary>
        int Execute(IEnumerable<DbCommand> commands)
        {
            var result = 0;
            using (var conn = OpenConnection())
            {
                using (var tx = conn.BeginTransaction())
                {
                    foreach (var cmd in commands)
                    {
                        cmd.Connection = conn;
                        cmd.Transaction = tx;
                        result += cmd.ExecuteNonQuery();
                    }
                    tx.Commit();
                }
            }
            return result;
        }
    }
}
